package com.ee.chat.api.task;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.ee.chat.api.common.CommonService;
import com.ee.chat.common.dao.*;
import com.ee.chat.common.entity.*;
import com.ee.chat.common.serconfig.MyNeteaseApp;
import com.ee.chat.common.service.IGeneralService;
import com.ee.chat.common.service.IYieldService;
import com.ndhutu.ee.util.DateUtil;
import com.ndhutu.ee.util.netease.call.Call2Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author Shane
 * @Date 2019/10/30 0:23
 */
@Component
public class CallTask {
    private Logger log = LoggerFactory.getLogger(CallTask.class);

    @Value("${ali.rocketmq.call.topic}")
    String call_topic;
    @Value("${ali.rocketmq.call.tag}")
    String call_tag;

    @Resource(name = "stringRedisTemplate")
    StringRedisTemplate redisTemplate;
    @Resource
    UserCallLogMapper userCallLogMapper;
    @Resource
    UserWalletMapper userWalletMapper;
    @Resource
    UserConsumeLogMapper userConsumeLogMapper;
    @Resource
    UserCoinLogMapper userCoinLogMapper;
    @Resource
    UserCreditLogMapper userCreditLogMapper;
    @Resource
    UserMapper userMapper;

    @Resource
    CommonService commonService;
    @Resource
    MyNeteaseApp app;
    @Resource
    IYieldService yieldService;
    @Resource
    IGeneralService generalService;
    @Resource
    ProducerBean producer;

    /**
     * 结束通话
     * @Author Shane
     * @Date 2019/11/2 18:26
     * @param channelId 频道id
     * @return
     */
    public void endCall(String channelId, Date endTime) throws Exception {
        UserCallLog userCallLog = userCallLogMapper.selectByChannelId(channelId);
        if(userCallLog.getEndTime() != null){
            return;
        }

        userCallLog.setEndTime(endTime);
        userCallLogMapper.updateById(userCallLog);

        //删除任务
        redisTemplate.opsForHash().delete("scheduler", channelId);

        //更新消费记录
        UserConsumeLog userConsumeLog = userConsumeLogMapper.selectByBuyIdAndType(userCallLog.getId(), 0);
        userConsumeLog.setCoin(userCallLog.getTotalCoin());
        userConsumeLog.setCredit(userCallLog.getTotalCredit());
        userConsumeLogMapper.updateById(userConsumeLog);

        UserCoinLog userCoinLog = userCoinLogMapper.selectByConsumIdAndType(userConsumeLog.getId(), 1);
        userCoinLog.setNumber(-userCallLog.getTotalCoin());
        userCoinLogMapper.updateById(userCoinLog);

        if(userCallLog.getTotalCredit() > 0){
            UserCreditLog userCreditLog = userCreditLogMapper.selectByConsumIdAndType(userConsumeLog.getId(), 0);
            userCreditLog.setNumber(userCallLog.getTotalCredit());
            userCreditLogMapper.updateById(userCreditLog);
        }

        //添加动态
        UserOperationLog userOperationLog = null;
        if(userCallLog.getHost() == 0){
            userOperationLog = new UserOperationLog(userCallLog.getFromUserId(), 0, 5, userCallLog.getToUserId(), userCallLog.getId());
        }else{
            userOperationLog = new UserOperationLog(userCallLog.getToUserId(), 0, 5, userCallLog.getFromUserId(), userCallLog.getId());
        }
        generalService.addUserOperationLog(userOperationLog, null);

        //更新用户状态
        User user = new User();
        user.setId(userCallLog.getFromUserId());
        user.setOnline(3);  //在线
        userMapper.updateById(user);
        User hostUser = new User();
        hostUser.setId(userCallLog.getToUserId());
        hostUser.setOnline(3);  //在线
        userMapper.updateById(hostUser);

        //处理邀请奖励
        yieldService.handlePopYield(userConsumeLog.getToUserId(), 1, userConsumeLog.getCredit(), userConsumeLog.getId());
    }

    /**
     * 添加定时任务
     * @Author Shane
     * @Date 2019/10/3020:31
     * @param
     * @return
     **/
    public void addCallTask(String channelId, Date date) throws Exception{
        String time = DateUtil.format(date, DateUtil.pattern_general);
        Map<String, Object> channelTask = new HashMap<>();

        channelTask.put("channelId", channelId);
        channelTask.put("beginTime", time);     //任务开始时间
        channelTask.put("num", "1");    //执行次数
        channelTask.put("lastTheoryTime", time);    //上一次的理论时间
        channelTask.put("lastDealtTime", time); //上一次执行时间
        channelTask.put("cost", "0");   //未收费

        redisTemplate.opsForHash().put("scheduler", channelId, JSONObject.toJSONString(channelTask));

        //防止视频通话接听后直接挂断造成没有扣费
        callFee(channelTask);
    }

    /**
     * 每1秒执行一次，生产用户视频通话收费任务消息
     *  经测试，1服可满足1秒执行1600次视频通话的测试
     * @Author Shane
     * @Date 2019/10/30 20:37
     * @param
     * @return
     **/
    @Scheduled(cron = "0/1 * * * * ?")
    @Transactional(rollbackFor = Exception.class)
    public void produceCallMQ() throws Exception {
        if (!commonService.getIpStatus()) {
            return;
        }

        Set<Object> channelIds = redisTemplate.opsForHash().keys("scheduler");
        Date date = new Date();

        for(Object channelIdObj : channelIds){
            String channelId = channelIdObj+"";
            Object jsonObj = redisTemplate.opsForHash().get("scheduler", channelId);

            if(jsonObj == null){
                continue;
            }

            Map<String, Object> map = JSONObject.parseObject(jsonObj+"", Map.class);
            Date lastTime = DateUtil.parse(map.get("lastDealtTime")+"", DateUtil.pattern_general);
            long timeDiff = date.getTime() - lastTime.getTime();
            int cost = Integer.parseInt(map.get("cost")+"");

            boolean flag = false;
            if(timeDiff > 10 * 1000 && cost == 0){
                flag = true;
            }else if(timeDiff >= 1*60*1000){
                flag = true;
            }

            if(!flag){
                continue;
            }

            //生产音视频通话消息
            Message message = new Message(call_topic, app.getIdPrefix() + "-" + call_tag, jsonObj.toString().getBytes());
            producer.sendOneway(message);
        }
    }

    /**
     * 通话扣费
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public void callFee(Map<String, Object> map) throws Exception {
        Date date = new Date();

        String channelId = map.get("channelId")+"";
        String key = "data:call:"+channelId;
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        if(lock == false){
            return;
        }

        Date lastTime = DateUtil.parse(map.get("lastDealtTime")+"", DateUtil.pattern_general);
        long timeDiff = date.getTime() - lastTime.getTime();
        int cost = Integer.parseInt(map.get("cost")+"");

        UserCallLog userCallLog = userCallLogMapper.selectByChannelId(channelId);
        if(userCallLog.getEndTime() != null){
            redisTemplate.opsForHash().delete("scheduler", channelId);

            //删除状态锁
            redisTemplate.delete(key);
            return;
        }

        Integer userId, hostUserId;
        if(userCallLog.getHost() == 0){
            userId = userCallLog.getFromUserId();
            hostUserId = userCallLog.getToUserId();
        }else{
            hostUserId = userCallLog.getFromUserId();
            userId = userCallLog.getToUserId();
        }
        //主播在通话10秒后才获取收益
        if(timeDiff > 10 * 1000 && cost == 0){
            if(userCallLog.getTotalCredit() == 0){
                map.put("cost", "1");
                redisTemplate.opsForHash().put("scheduler", channelId, JSONObject.toJSONString(map));

                //更新消费记录
                userCallLog.setTotalCredit(userCallLog.getEarn());
                userCallLogMapper.updateById(userCallLog);

                UserConsumeLog userConsumeLog = userConsumeLogMapper.selectByBuyIdAndType(userCallLog.getId(), 0);
                userConsumeLog.setCredit(userCallLog.getEarn());
                userConsumeLogMapper.updateById(userConsumeLog);

                //更新主播钱包
                yieldService.updateUserWallet(hostUserId, userConsumeLog.getId(), 0, userCallLog.getEarn(), "视频通话", 1);

                //删除状态锁
                redisTemplate.delete(key);

                log.info("频道"+channelId+"通话第10秒，主播获得第"+map.get("num")+"分钟积分");
                return;
            }
        }

        if(timeDiff >= 1*60*1000){
            if(timeDiff >= 2*60*1000){
                log.info("存在任务超过指定时间2分钟未执行，channel："+channelId+"，task："+JSONObject.toJSONString(map));
            }

            //检查房间状态
            Map<String, Object> rooms = Call2Util.rooms(channelId, app);
            Integer stats = Integer.parseInt(rooms.get("stats")+"");
            if(stats == 3 || stats == 4){ //通话已结束
                log.info(channelId+"通讯双方均异常结束，后台强制结束通话");

                //结束通话
                endCall(channelId, date);

                //删除任务
                redisTemplate.opsForHash().delete("scheduler", channelId);

                //删除状态锁
                redisTemplate.delete(key);
                return;
            }

            int n = (int)(timeDiff/(1*60*1000));
            int coin = n * userCallLog.getCharge();
            double credit = n * userCallLog.getEarn();

            UserWallet userWallet = userWalletMapper.getByUserId(userId);
            if(userWallet.getActiveCoin() < coin){    //结束通话
                log.info(channelId+"频道中"+userWallet.getUserId()+"P币不足，后台强制结束通话");

                String desc = "";
                try{
                    log.info("踢出用户"+userId);
                    desc = channelId+"房间踢出用户"+userId+"失败";

                    Call2Util.kickMembers(channelId, app.getIdPrefix()+userId, app);
                }catch (Exception e){
                    log.error(desc, e);
                }

                //结束通话
                endCall(channelId, date);

                //删除任务
                redisTemplate.opsForHash().delete("scheduler", channelId);

                //删除状态锁
                redisTemplate.delete(key);
                return;
            }

            //更新缓存
            map.put("num", Integer.parseInt(map.get("num")+"") + n);
            Date lastTheoryTime = DateUtil.parse(map.get("lastTheoryTime")+"");
            Calendar cal = Calendar.getInstance();
            cal.setTime(lastTheoryTime);
            cal.add(Calendar.MINUTE, n);
            map.put("lastTheoryTime", DateUtil.format(cal.getTime(), DateUtil.pattern_general));
            map.put("lastDealtTime", DateUtil.format(date, DateUtil.pattern_general));
            map.put("rich", "0");
            redisTemplate.opsForHash().put("scheduler", channelId, JSONObject.toJSONString(map));

            //更新记录
            userCallLog.setTotalCoin(userCallLog.getTotalCoin() + coin);
            userCallLog.setTotalCredit(userCallLog.getTotalCredit() + credit);
            userCallLogMapper.updateById(userCallLog);

            //更新钱包
            userWallet.setActiveCoin(userWallet.getActiveCoin() - coin);
            userWalletMapper.updateById(userWallet);
            UserWallet hostWallet = userWalletMapper.getByUserId(hostUserId);
            hostWallet.setTotalCredit(hostWallet.getTotalCredit() + credit);
            hostWallet.setActiveCredit(hostWallet.getActiveCredit() + credit);
            userWalletMapper.updateById(hostWallet);

            //更新消费记录
            UserConsumeLog userConsumeLog = userConsumeLogMapper.selectByBuyIdAndType(userCallLog.getId(), 0);
            userConsumeLog.setCoin(userCallLog.getTotalCoin());
            userConsumeLog.setCredit(userCallLog.getTotalCredit());
            userConsumeLogMapper.updateById(userConsumeLog);

            UserCoinLog userCoinLog = userCoinLogMapper.selectByConsumIdAndType(userConsumeLog.getId(), 1);
            userCoinLog.setNumber(-userConsumeLog.getCoin());
            userCoinLogMapper.updateById(userCoinLog);

            UserCreditLog userCreditLog = userCreditLogMapper.selectByConsumIdAndType(userConsumeLog.getId(), 0);
            userCreditLog.setNumber(userConsumeLog.getCredit());
            userCreditLogMapper.updateById(userCreditLog);

            log.info("频道id"+channelId+"，主播获得第"+map.get("num")+"分钟通话积分");
        }

        //删除状态锁
        redisTemplate.delete(key);
    }
}
